#include "com.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

com_t serial, rs485;

uint8_t buffer[COM_TX_BUFFER_SIZE];

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
#if ENABLE_OS_SUPPORT
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
#endif
    if (huart->Instance == serial.uart->Instance)
    {
#if ENABLE_OS_SUPPORT
        xSemaphoreGiveFromISR(com_extern.rx_complete, &xHigherPriorityTaskWoken);
#else
        serial.rx_complete = 1;
#endif
        serial.size = Size;
    }
    else if (huart->Instance == rs485.uart->Instance)
    {
#if ENABLE_OS_SUPPORT
        xSemaphoreGiveFromISR(com_internal.rx_complete, &xHigherPriorityTaskWoken);
#else
        rs485.rx_complete = 1;
#endif
        rs485.size = Size;
    }
#if ENABLE_OS_SUPPORT
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
#if ENABLE_OS_SUPPORT
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
#endif
    if (huart->Instance == serial.uart->Instance)
    {
#if ENABLE_OS_SUPPORT
        xSemaphoreGiveFromISR(com_extern.tx_complete, &xHigherPriorityTaskWoken);
#else
        serial.tx_complete = 1;
#endif
    }
    else if (huart->Instance == rs485.uart->Instance)
    {
#if ENABLE_OS_SUPPORT
        xSemaphoreGiveFromISR(com_internal.tx_complete, &xHigherPriorityTaskWoken);
#else
        rs485.tx_complete = 1;
#endif
    }
#if ENABLE_OS_SUPPORT
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == serial.uart->Instance)
    {
        HAL_UARTEx_ReceiveToIdle_DMA(serial.uart, serial.payload, COM_RX_BUFFER_SIZE);
    }
    else if (huart->Instance == rs485.uart->Instance)
    {
        HAL_UARTEx_ReceiveToIdle_DMA(rs485.uart, rs485.payload, COM_RX_BUFFER_SIZE);
    }
}

void Serial_Init(UART_HandleTypeDef *huart, com_rx_callback_t callback)
{
    serial.uart = huart;
    serial.callback = callback;
    HAL_UARTEx_ReceiveToIdle_DMA(serial.uart, serial.payload, COM_RX_BUFFER_SIZE);
#if ENABLE_OS_SUPPORT
    com_extern.tx_complete = xSemaphoreCreateBinary();
    com_extern.rx_complete = xSemaphoreCreateBinary();
    xSemaphoreGive(com_extern.tx_complete);
#else
    serial.tx_complete = 1;
#endif
}

void Serial_Run()
{
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(com_extern.rx_complete, portMAX_DELAY);
    if (com_extern.callback != NULL)
        com_extern.callback();
    HAL_UARTEx_ReceiveToIdle_DMA(serial.uart, serial.payload, COM_RX_BUFFER_SIZE);
#else
    if (serial.rx_complete)
    {
        if (serial.callback != NULL)
            serial.callback();
        serial.rx_complete = 0;
        HAL_UARTEx_ReceiveToIdle_DMA(serial.uart, serial.payload, COM_RX_BUFFER_SIZE);
    }
#endif
}

void Serial_Print(const char *fmt, ...)
{
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(com_extern.tx_complete, portMAX_DELAY);
#else
    while (!serial.tx_complete)
        continue;
    serial.tx_complete = 0;
#endif
    va_list args;
    va_start(args, fmt);
    vsprintf((char *)buffer, fmt, args);
    va_end(args);
    HAL_UART_Transmit_DMA(serial.uart, buffer, strlen((char *)buffer));
}

void Serial_Send(uint8_t *data, uint16_t size)
{
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(com_extern.tx_complete, portMAX_DELAY);
#else
    while (!serial.tx_complete)
        continue;
    serial.tx_complete = 0;
#endif
    HAL_UART_Transmit_DMA(serial.uart, data, size);
}

void RS485_Init(UART_HandleTypeDef *huart, com_rx_callback_t callback)
{
    rs485.uart = huart;
    rs485.callback = callback;
    HAL_UARTEx_ReceiveToIdle_DMA(rs485.uart, rs485.payload, COM_RX_BUFFER_SIZE);
#if ENABLE_OS_SUPPORT
    com_internal.tx_complete = xSemaphoreCreateBinary();
    com_internal.rx_complete = xSemaphoreCreateBinary();
    xSemaphoreGive(com_internal.tx_complete);
#else
    rs485.tx_complete = 1;
#endif
}

void RS485_Run()
{
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(com_internal.rx_complete, portMAX_DELAY);
    if (com_internal.callback != NULL)
        com_internal.callback();
    HAL_UARTEx_ReceiveToIdle_DMA(rs485.uart, rs485.payload, COM_RX_BUFFER_SIZE);
#else
    if (rs485.rx_complete)
    {
        if (rs485.callback != NULL)
            rs485.callback();
        rs485.rx_complete = 0;
        HAL_UARTEx_ReceiveToIdle_DMA(rs485.uart, rs485.payload, COM_RX_BUFFER_SIZE);
    }
#endif
}

void RS485_Send(uint8_t *data, uint16_t size)
{
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(com_internal.tx_complete, portMAX_DELAY);
#else
    while (!rs485.tx_complete)
        continue;
    rs485.tx_complete = 0;
#endif
    HAL_UART_Transmit_DMA(rs485.uart, data, size);
}
